﻿using MongoDB.Bson;
using MongoDB.Driver;
using Sustainalytics.Auth.Claims;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.DataLayer;
using Sustainalytics.Entities.RiskRatings;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Cors;

namespace Sustainalytics.RiskRatings.Service.Controllers
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/companies")]
    public class ListingController : ApiController
    {
        private readonly IMongoCollection<RiskRatingResearchProfile> _riskProfileCollection;
        private readonly IMongoCollection<CoreRiskRatingResearchProfile> _coreRiskProfileCollection;
        private readonly IMongoCollection<CorporateData> _corporateDataCollection;


        public ListingController(
           IMongoCollection<RiskRatingResearchProfile> riskProfilecollection,
           IMongoCollection<CoreRiskRatingResearchProfile> coreRiskProfileCollection,
           IMongoCollection<CorporateData> corporateDataCollection
           )
        {
            _riskProfileCollection = riskProfilecollection;
            _coreRiskProfileCollection = coreRiskProfileCollection;
            _corporateDataCollection = corporateDataCollection;
        }

        private List<FilterDefinition<RiskRatingResearchProfile>> getFilterOptionsForRiskProfile(RiskRatingListFilters filters, Guid[] avalilableCompanies)
        {
            var filtersCompanies = Builders<RiskRatingResearchProfile>.Filter;
            var esgFiltersList = new List<FilterDefinition<RiskRatingResearchProfile>> { FilterDefinition<RiskRatingResearchProfile>.Empty };


            if (!avalilableCompanies.IsNullOrEmpty())
            {
                var filterCompanyIds = Builders<RiskRatingResearchProfile>.Filter.In(x => x.Id, avalilableCompanies);
                esgFiltersList.Add(filterCompanyIds);
            }

            if (!filters.CompanyName.IsNullOrEmpty())
            {
                var filterCompany = filtersCompanies.Regex("CompanyName", new BsonRegularExpression(Regex.Escape(filters.CompanyName.ToLowerInvariant()), "i"));
                esgFiltersList.Add(filterCompany);
            }

            if (!filters.SubPeerGroup.IsNullOrEmpty())
            {
                var filterSubPeerGroup = filtersCompanies.Regex("SubPeerGroup", new BsonRegularExpression(Regex.Escape(filters.SubPeerGroup.ToLowerInvariant()), "i"));
                esgFiltersList.Add(filterSubPeerGroup);
            }

            if (!filters.Assessments.IsNullOrEmpty())
            {
                var filterSubPeerGroup = filtersCompanies.In(x => x.Assessment.Value, filters.Assessments);
                esgFiltersList.Add(filterSubPeerGroup);
            }

            return esgFiltersList;
        }

        private List<FilterDefinition<CoreRiskRatingResearchProfile>> getFilterOptionsForRiskProfileCore(RiskRatingListFilters filters, Guid[] avalilableCompanies)
        {
            var filtersCompanies = Builders<CoreRiskRatingResearchProfile>.Filter;
            var esgFiltersList = new List<FilterDefinition<CoreRiskRatingResearchProfile>> { FilterDefinition<CoreRiskRatingResearchProfile>.Empty };

            if (!avalilableCompanies.IsNullOrEmpty())
            {
                var filterCompanyIds = Builders<CoreRiskRatingResearchProfile>.Filter.In(x => x.Id, avalilableCompanies);
                esgFiltersList.Add(filterCompanyIds);
            }

            if (!filters.CompanyName.IsNullOrEmpty())
            {
                var filterCompany = filtersCompanies.Regex("CompanyName", new BsonRegularExpression(Regex.Escape(filters.CompanyName.ToLowerInvariant()), "i"));
                esgFiltersList.Add(filterCompany);
            }

            if (!filters.SubPeerGroup.IsNullOrEmpty())
            {
                var filterSubPeerGroup = filtersCompanies.Regex("SubPeerGroup", new BsonRegularExpression(Regex.Escape(filters.SubPeerGroup.ToLowerInvariant()), "i"));
                esgFiltersList.Add(filterSubPeerGroup);
            }

            if (!filters.Assessments.IsNullOrEmpty())
            {
                var filterSubPeerGroup = filtersCompanies.In(x => x.Assessment.Value, filters.Assessments);
                esgFiltersList.Add(filterSubPeerGroup);
            }

            return esgFiltersList;
        }

        [Route("")]
        [InjectUniverse("universeIds", AccountFeature.RiskRatingsResearch)]
        public async Task<IHttpActionResult> Get(
            [FromUri] RiskRatingListFilters filters,
            int skip = 0,
            int take = 20,
            bool getCount = false,
            bool orderAscending = true,
            string orderBy = "CompanyName",
            [FromUri]int[] universeIds = null)
        {
            var corporateFilter = Builders<CorporateData>.Filter;

            var filter = (corporateFilter.Eq(x => x.CoreRiskResearchState.ResearchState, ResearchState.ResearchAvailable) |
                    corporateFilter.Eq(x => x.RiskRatingResearchState.ResearchState, ResearchState.ResearchAvailable)) &
                    corporateFilter.AnyIn(x => x.UniverseIds, universeIds);

            var availableCompanies = await _corporateDataCollection
             .Find(filter)
             .Project<CorporateData>(
                 Builders<CorporateData>.Projection
                 .Include(x => x.Id)
                 .Include(x => x.CompanyName)
                 .Include(x => x.SubPeerGroup))
             .ToListAsync()
             .ConfigureAwait(false);

            var companiesIds = availableCompanies.Select(x => x.Id).ToArray();
            var filtersForCompanies = getFilterOptionsForRiskProfile(filters, companiesIds);
            var filtersForCoreCompanies = getFilterOptionsForRiskProfileCore(filters, companiesIds);

            var riskProfileFilter = Builders<RiskRatingResearchProfile>.Filter;
            var coreRiskProfileFilter = Builders<CoreRiskRatingResearchProfile>.Filter;

            if (getCount)
            {
                var countRR = await _riskProfileCollection.Find(riskProfileFilter.And(filtersForCompanies)).CountAsync().ConfigureAwait(false);
                var countCoreRR = await _coreRiskProfileCollection.Find(coreRiskProfileFilter.And(filtersForCoreCompanies)).CountAsync().ConfigureAwait(false);
                var totalCount = countRR + countCoreRR;

                return Ok(new { companyCount = totalCount });
            }

            FindOptions options = new FindOptions() { Collation = new Collation(locale: "en", caseLevel: true, strength: CollationStrength.Primary) };

            var cursorRR = _riskProfileCollection.Find(riskProfileFilter.And(filtersForCompanies), options);
            cursorRR = cursorRR.Project<RiskRatingResearchProfile>(
                Builders<RiskRatingResearchProfile>.Projection
                .Include(x => x.Id)
                .Include(x => x.CompanyName)
                .Include(x => x.Ticker)
                .Include(x => x.Exchange)
                .Include(x => x.LastUpdate)
                .Include(x => x.Assessment));

            var cursorCoreRR = _coreRiskProfileCollection.Find(coreRiskProfileFilter.And(filtersForCoreCompanies), options);
            cursorCoreRR = cursorCoreRR.Project<CoreRiskRatingResearchProfile>(
                Builders<CoreRiskRatingResearchProfile>.Projection
                .Include(x => x.Id)
                .Include(x => x.CompanyName)
                .Include(x => x.Ticker)
                .Include(x => x.Exchange)
                .Include(x => x.LastUpdate)
                .Include(x => x.Assessment));

            var companiesRR = await cursorRR.ToListAsync().ConfigureAwait(false);

            var resultRR = companiesRR.Select(x => new
            {
                x.Id,
                x.CompanyName,
                x.Ticker,
                x.Exchange,
                SubPeerGroup = availableCompanies.Where(a => a.Id == x.Id).Select(c => c.SubPeerGroup),
                x.LastUpdate,
                Assessment = x.Assessment.ToString(),
                IsEnabled = availableCompanies != null && availableCompanies.Select(comp => comp.Id).Contains(x.Id)
            });

            var companiesCoreRR = await cursorCoreRR.ToListAsync().ConfigureAwait(false);
            var resultCoreRR = companiesCoreRR.Select(x => new
            {
                x.Id,
                x.CompanyName,
                x.Ticker,
                x.Exchange,
                SubPeerGroup = availableCompanies.Where(a => a.Id == x.Id).Select(c => c.SubPeerGroup),
                x.LastUpdate,
                Assessment = x.Assessment.ToString(),
                IsEnabled = availableCompanies != null && availableCompanies.Select(comp => comp.Id).Contains(x.Id)
            });

            var joinedResult = resultRR.Concat(resultCoreRR);

            var sortDefinition = orderAscending ?
                joinedResult = joinedResult.OrderBy(s => s.GetType().GetProperty(orderBy).GetValue(s)).ToList() :
                joinedResult = joinedResult.OrderByDescending(s => s.GetType().GetProperty(orderBy).GetValue(s)).ToList();

            return Ok(joinedResult.Skip(skip).Take(take));
        }
    }
}